<!--
 * @Author: liaoyu1992 1519778181@qq.com
 * @Date: 2022-11-05 12:49:58
 * @LastEditors: liaoyu1992 1519778181@qq.com
 * @LastEditTime: 2022-11-11 21:59:53
 * @FilePath: \kgantt\core\components\gantt\TaskGantt.vue
 * @Description: 
-->
<template>
  <div class="kgantt-ganttVerticalContainer" ref="verticalGanttContainerRef" dir="ltr">
    <svg
      xmlns="http://www.w3.org/2000/svg"
      :width="svgWidth"
      :height="headerHeight"
      :fontFamily="fontFamily"
    >
      <Calendar
        :date-setup="dateSetup"
        :locale="locale"
        :view-mode="viewMode"
        :rtl="rtl"
        :header-height="headerHeight"
        :column-width="columnWidth"
        :font-family="fontFamily"
        :font-size="fontSize"
      />
    </svg>
    <div
      ref="horizontalContainerRef"
      class="kgantt-horizontalContainer"
      :style="
        ganttHeight
          ? 'height:' + ganttHeight + 'px;width:' + svgWidth + 'px'
          : 'width:' + svgWidth + 'px'
      "
    >
      <svg
        xmlns="http://www.w3.org/2000/svg"
        :width="svgWidth"
        :height="rowHeight * tasks.length"
        :font-family="fontFamily"
        ref="ganttSVGRef"
      >
        <Grid
          :tasks="tasks"
          :dates="dates"
          :svg-width="svgWidth"
          :row-height="rowHeight"
          :column-width="columnWidth"
          :today-color="todayColor"
          :rtl="rtl"
        />
        <TaskGanttContent
          :tasks="barTasks"
          :dates="dates"
          :gantt-event="ganttEvent"
          :selected-task="selectedTask"
          :row-height="rowHeight"
          :column-width="columnWidth"
          :time-step="timeStep"
          :svg-width="svgWidth"
          :task-height="taskHeight"
          :arrow-color="arrowColor"
          :arrow-indent="arrowIndent"
          :font-size="fontSize"
          :font-family="fontFamily"
          :rtl="rtl"
          @handle-bar-event-start="handleBarEventStart"
        />
      </svg>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch, nextTick, PropType } from "vue";
import Calendar from "../calendar/Calendar.vue";
import Grid from "../grid/Grid.vue";
import TaskGanttContent from "./TaskGanttContent.vue";
import { handleTaskBySVGMouseEvent } from "../../helpers/barHelper";
import { Task, ViewMode } from "../../types/publicTypes";
import { DateSetup } from "../../types/dateSetup";
import {
  BarMoveAction,
  GanttContentMoveAction,
  GanttEvent,
} from "../../types/ganttTaskActions";
import { BarTask } from "../../types/barTask";
import { isKeyboardEvent } from "../../helpers/otherHelper";
const props = defineProps({
  tasks: {
    type: Array as PropType<Task[]>,
    default: () => {},
  },
  barTasks: {
    type: Array as PropType<BarTask[]>,
    default: () => [],
  },
  dates: {
    type: Array as PropType<Date[]>,
    default: () => {},
  },
  svgWidth: {
    type: Number,
    default: 0,
  },
  rowHeight: {
    type: Number,
    default: 0,
  },
  columnWidth: {
    type: Number,
    default: 0,
  },
  todayColor: {
    type: String,
    default: "",
  },
  rtl: {
    type: Boolean,
    default: false,
  },
  dateSetup: {
    type: Object as PropType<DateSetup>,
    default: {},
  },
  locale: {
    type: String,
    default: "",
  },
  viewMode: {
    type: String as PropType<ViewMode>,
    default: {},
  },
  headerHeight: {
    type: Number,
    default: 0,
  },
  fontFamily: {
    type: String,
    default: "",
  },
  fontSize: {
    type: String,
    default: "",
  },
  selectedTask: {
    type: Object as PropType<BarTask>,
    default: {},
  },
  timeStep: {
    type: Number,
    default: 0,
  },
  taskHeight: {
    type: Number,
    default: 0,
  },
  arrowColor: {
    type: String,
    default: "",
  },
  arrowIndent: {
    type: Number,
    default: 0,
  },
  ganttHeight: {
    type: Number,
    default: 0,
  },
  scrollY: {
    type: Number,
    default: 0,
  },
  scrollX: {
    type: Number,
    default: 0,
  },
});
const emits = defineEmits([
  "setGanttEvent",
  "onDateChange",
  "onProgressChange",
  "setFailedTask",
  "setSelectedTask",
  "onDelete",
  "onDoubleClick",
  "onClick",
  "handleDateChange",
]);
const ganttSVGRef = ref();
const point = ref<DOMPoint>();
const xStep = ref(0);
const initEventX1Delta = ref(0);
const isMoving = ref(false);
const horizontalContainerRef = ref();
const verticalGanttContainerRef = ref();
const ganttEvent = ref<GanttEvent>({ action: "" as BarMoveAction });
const initXStep = () => {
  const dateDelta =
    props.dates[1].getTime() -
    props.dates[0].getTime() -
    props.dates[1].getTimezoneOffset() * 60 * 1000 +
    props.dates[0].getTimezoneOffset() * 60 * 1000;
  const newXStep = (props.timeStep * props.columnWidth) / dateDelta;
  xStep.value = newXStep;
};
const handleMouseMove = async (event: MouseEvent) => {
  if (!ganttEvent.value.changedTask || !point.value || !ganttSVGRef.value)
    return;
  event.preventDefault();
  point.value.x = event.clientX;
  const cursor = point.value.matrixTransform(
    ganttSVGRef?.value.getScreenCTM()?.inverse()
  );

  const { isChanged, changedTask } = handleTaskBySVGMouseEvent(
    cursor.x,
    ganttEvent.value.action as BarMoveAction,
    ganttEvent.value.changedTask,
    xStep.value,
    props.timeStep,
    initEventX1Delta.value,
    props.rtl
  );
  if (isChanged) {
    ganttEvent.value = {
      action: ganttEvent.value.action,
      changedTask,
      originalSelectedTask: ganttEvent.value.originalSelectedTask,
    };
    emits("setGanttEvent", {
      action: ganttEvent.value.action,
      changedTask,
      originalSelectedTask: ganttEvent.value.originalSelectedTask,
    });
  }
};
const handleMouseUp = async (event: MouseEvent) => {
  const { action, originalSelectedTask, changedTask } = ganttEvent.value;
  if (
    !changedTask ||
    !point.value ||
    !ganttSVGRef.value ||
    !originalSelectedTask
  )
    return;
  event.preventDefault();
  point.value.x = event.clientX;
  const cursor = point.value.matrixTransform(
    ganttSVGRef.value.getScreenCTM()?.inverse()
  );
  const { changedTask: newChangedTask } = handleTaskBySVGMouseEvent(
    cursor.x,
    action as BarMoveAction,
    changedTask,
    xStep.value,
    props.timeStep,
    initEventX1Delta.value,
    props.rtl
  );
  const isNotLikeOriginal =
    originalSelectedTask.start !== newChangedTask.start ||
    originalSelectedTask.end !== newChangedTask.end ||
    originalSelectedTask.progress !== newChangedTask.progress;

  ganttSVGRef.value.removeEventListener("mousemove", handleMouseMove);
  ganttSVGRef.value.removeEventListener("mouseup", handleMouseUp);
  ganttEvent.value = { action: "" };
  emits("setGanttEvent", { action: "" });
  isMoving.value = false;
  let operationSuccess = true;
  if (
    (action === "move" || action === "end" || action === "start") &&
    isNotLikeOriginal
  ) {
    emits("handleDateChange", newChangedTask);
    try {
      emits(
        "onDateChange",
        newChangedTask,
        newChangedTask.barChildren,
        (result: boolean) => {
          if (result !== undefined) {
            operationSuccess = result;
          }
        }
      );
    } catch (error) {
      operationSuccess = false;
    }
  } else if (isNotLikeOriginal) {
    try {
      emits(
        "onProgressChange",
        newChangedTask,
        newChangedTask.barChildren,
        (result: boolean) => {
          if (result !== undefined) {
            operationSuccess = result;
          }
        }
      );
    } catch (error) {
      operationSuccess = false;
    }
  }
  if (!operationSuccess) {
    emits("setFailedTask", originalSelectedTask);
  }
};
const handleBarEventStart = async (
  action: GanttContentMoveAction,
  task: BarTask,
  event?: MouseEvent | KeyboardEvent
) => {
  if (!event) {
    if (action === "select") {
      emits("setSelectedTask", task.id);
    }
  }
  // Keyboard events
  else if (isKeyboardEvent(event)) {
    if (action === "delete") {
      try {
        emits("onDelete", task, (result: any) => {
          if (result !== undefined && result) {
            ganttEvent.value = { action, changedTask: task };
            emits("setGanttEvent", { action, changedTask: task });
          }
        });
      } catch (error) {
        console.error("Error on Delete. " + error);
      }
    }
  }
  // Mouse Events
  else if (action === "mouseenter") {
    if (!ganttEvent.value.action) {
      ganttEvent.value = {
        action,
        changedTask: task,
        originalSelectedTask: task,
      };
      emits("setGanttEvent", {
        action,
        changedTask: task,
        originalSelectedTask: task,
      });
    }
  } else if (action === "mouseleave") {
    if (ganttEvent.value.action === "mouseenter") {
      ganttEvent.value = { action: "" };
      emits("setGanttEvent", { action: "" });
    }
  } else if (action === "dblclick") {
    emits("onDoubleClick", task);
  } else if (action === "click") {
    emits("onClick", task);
  }
  // Change task event start
  else if (action === "move") {
    if (!ganttSVGRef.value || !point.value) return;
    point.value.x = event.clientX;
    const cursor = point.value.matrixTransform(
      ganttSVGRef.value.getScreenCTM()?.inverse()
    );
    initEventX1Delta.value = cursor.x - task.x1;
    ganttEvent.value = {
      action,
      changedTask: task,
      originalSelectedTask: task,
    };
    emits("setGanttEvent", {
      action,
      changedTask: task,
      originalSelectedTask: task,
    });
  } else {
    ganttEvent.value = {
      action,
      changedTask: task,
      originalSelectedTask: task,
    };
    emits("setGanttEvent", {
      action,
      changedTask: task,
      originalSelectedTask: task,
    });
  }
};
watch(
  () => ganttEvent.value,
  () => {
    if (
      !isMoving.value &&
      (ganttEvent.value.action === "move" ||
        ganttEvent.value.action === "end" ||
        ganttEvent.value.action === "start" ||
        ganttEvent.value.action === "progress") &&
      ganttSVGRef.value
    ) {
      ganttSVGRef.value.addEventListener("mousemove", handleMouseMove);
      ganttSVGRef.value.addEventListener("mouseup", handleMouseUp);
      isMoving.value = true;
    }
  }
);
watch(
  () => [props.scrollX, props.scrollY],
  () => {
    if (horizontalContainerRef.value) {
      horizontalContainerRef.value.scrollTop = props.scrollY;
    }
    if (verticalGanttContainerRef.value) {
      verticalGanttContainerRef.value.scrollLeft = props.scrollX;
    }
  }
);
watch(
  () => [props.viewMode,props.tasks],
  () => {
    initXStep();
  }
);
onMounted(() => {
  initXStep();
  nextTick(() => {
    point.value = ganttSVGRef.value.createSVGPoint();
    if (horizontalContainerRef.value) {
      horizontalContainerRef.value.scrollTop = props.scrollY;
    }
    if (verticalGanttContainerRef.value) {
      verticalGanttContainerRef.value.scrollLeft = scrollX;
    }
  });
});
</script>